/***************************************************************************//**
 * @file  msdscsi.h
 * @brief SCSI interface for Mass Storage Devices (MSD).
 * @author Energy Micro AS
 * @version 2.0.1
 *******************************************************************************
 * @section License
 * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
 *******************************************************************************
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 * 4. The source and compiled code may only be used on Energy Micro "EFM32"
 *    microcontrollers and "EFR4" radios.
 *
 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
 * obligation to support this Software. Energy Micro AS is providing the
 * Software "AS IS", with no express or implied warranties of any kind,
 * including, but not limited to, any implied warranties of merchantability
 * or fitness for any particular purpose or warranties against infringement
 * of any proprietary rights of a third party.
 *
 * Energy Micro AS will not be liable for any consequential, incidental, or
 * special damages, or any other relief, or for any claim by any third party,
 * arising from your use of this Software.
 *
 *****************************************************************************/
#ifndef __MSDSCSI_H
#define __MSDSCSI_H

/***************************************************************************//**
 * @addtogroup Drivers
 * @{
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#define SCSI_TESTUNIT_READY   0x00  /**< SCSI Test Unit Ready command opcode. */
#define SCSI_REQUESTSENSE     0x03  /**< SCSI Request Sense command opcode.   */
#define SCSI_INQUIRY          0x12  /**< SCSI Inquiry command opcode.         */
#define SCSI_STARTSTOP_UNIT   0x1B  /**< SCSI Start Stop Unit command opcode. */
#define SCSI_READCAPACITY     0x25  /**< SCSI Read Capacity command opcode.   */
#define SCSI_READ10           0x28  /**< SCSI Read (10) command opcode.       */
#define SCSI_WRITE10          0x2A  /**< SCSI Write (10) command opcode.      */

#define SCSI_READ10_LEN             10U /**< SCSI Read (10) CBD length.       */
#define SCSI_WRITE10_LEN            10U /**< SCSI Write (10) CDB length.      */
#define SCSI_INQUIRYDATA_LEN        36U /**< SCSI Inquiry response data length. */
#define SCSI_REQUESTSENSE_LEN       6U  /**< SCSI Request Sense CDB length.   */
#define SCSI_REQUESTSENSEDATA_LEN   18U /**< SCSI Request Sense response data length. */
#define SCSI_READCAPACITY_LEN       10U /**< SCSI Read Capacity CDB length.   */
#define SCSI_READCAPACITYDATA_LEN   8U  /**< SCSI Read Capacity response data length. */

/**************************************************************************//**
 * @brief SCSI Inquiry Command Descriptor Block (CDB) typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  uint8_t   OpCode;               /**< Command opcode.                        */
  struct
  {
    uint8_t Evpd     : 1;         /**< Enable Vital Product data.             */
    uint8_t Obsolete : 1;         /**< Obsolete, expect any value.            */
    uint8_t Reserved : 6;         /**< Reserved, expect 0.                    */
  };
  uint8_t   PageCode;             /**< Pagecode.                              */
  uint16_t  AllocationLength;     /**< Number of inquiry data bytes requested.*/
  uint8_t   Control;              /**< Control byte.                          */
} __attribute__ ((packed)) MSDSCSI_Inquiry_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Inquiry response data typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  struct
  {
    uint8_t PeripheralDeviceType : 5; /**< Peripheral device type .           */
    uint8_t PeripheralQualifier  : 3; /**< Peripheral qualifier.              */
  };
  struct
  {
    uint8_t Reserved1 : 7;        /**< Reserved, expect 0.                    */
    uint8_t Removable : 1;        /**< Removable medium indicator.            */
  };
  uint8_t   Version;              /**< SPC standard version implementation.   */
  struct
  {
    uint8_t ResponseDataFormat : 4; /**< Response data format value.          */
    uint8_t HiSup              : 1; /**< Hierarchical Support indicator.      */
    uint8_t NormACA            : 1; /**< Normal Auto Contingent Allegiance support.*/
    uint8_t Obsolete1          : 2; /**< Obsolete, expect any value.          */
  };
  uint8_t   AdditionalLength;       /**< Number of additional inquiry bytes that follow.*/
  struct
  {
    uint8_t Protect         : 1;    /**< Protection information support.      */
    uint8_t Reserved2       : 2;    /**< Reserved, expect 0.                  */
    uint8_t ThirdPartyCode  : 1;    /**< Third Party copy command support.    */
    uint8_t Tpgs            : 2;    /**< Target Port Group Support.           */
    uint8_t Acc             : 1;    /**< Access Controls Coordinator.         */
    uint8_t Sccs            : 1;    /**< SCSI Controller Commands Supported.  */
  };
  struct
  {
    uint8_t Addr16      : 1;        /**< Wide SCSI address support.           */
    uint8_t Obsolete2   : 2;        /**< Obsolete, expect any value.          */
    uint8_t MChngr      : 1;        /**< Medium Changer indicator.            */
    uint8_t MultiP      : 1;        /**< Multi Port indicator.                */
    uint8_t Vs1         : 1;        /**< Vendor specific.                     */
    uint8_t EncServ     : 1;        /**< Enclosure Services support.          */
    uint8_t BQue        : 1;        /**< Full/Basic task management model.    */
  };
  struct
  {
    uint8_t Vs2         : 1;        /**< Vendor specific.                     */
    uint8_t CmdQue      : 1;        /**< Command queueing support.            */
    uint8_t Obsolete3   : 1;        /**< Obsolete, expect any value.          */
    uint8_t Linked      : 1;        /**< Linked commands support.             */
    uint8_t Sync        : 1;        /**< Synchronous data transfer support.   */
    uint8_t Wbus16      : 1;        /**< Wide SCSI data transfer support.     */
    uint8_t Obsolete4   : 2;        /**< Obsolete, expect any value.          */
  };
  uint8_t T10VendorId[ 8 ];         /**< T10 Vendor ID, 8 ASCII codes.        */
  uint8_t ProductId[ 16 ];          /**< Product ID, 16 ASCII codes.          */
  uint8_t ProductRevisionLevel[ 4 ];/**< Product revision level, 4 ASCII codes.*/
} __attribute__ ((packed)) MSDSCSI_InquiryData_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Request Sense Command Descriptor Block (CDB) typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  uint8_t   OpCode;               /**< Command opcode.                        */
  struct
  {
    uint8_t Desc      : 1;        /**< Descriptor format.                     */
    uint8_t Reserved1 : 7;        /**< Reserved, expect 0.                    */
  };
  uint8_t   Reserved2;            /**< Reserved, expect 0.                    */
  uint8_t   Reserved3;            /**< Reserved, expect 0.                    */
  uint8_t   AllocationLength;     /**< Number of sense data bytes requested.  */
  uint8_t   Control;              /**< Control byte.                          */
} __attribute__ ((packed)) MSDSCSI_RequestSense_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Request Sense response data typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  struct
  {
    uint8_t ResponseCode : 7;     /**< Response code, 0x70 = Current errors.  */
    uint8_t Valid        : 1;     /**< Valid, qualifier for Information field.*/
  };
  uint8_t   Obsolete;             /**< Obsolete, expect any value.            */
  struct
  {
    uint8_t SenseKey : 4;         /**< Generic error information.             */
    uint8_t Reserved : 1;         /**< Reserved, expect 0.                    */
    uint8_t Ili : 1;              /**< Incorrect length indicator.            */
    uint8_t Eom : 1;              /**< End of medium indicator.               */
    uint8_t FileMark : 1;         /**< Filemark indicator.                    */
  };
  uint32_t  Information;          /**< Information field.                     */
  uint8_t   AdditionalLength;     /**< Number of additional sense bytes that follow.*/
  uint32_t  CmdSpecificInfo;      /**< Command specific information field.    */
  uint8_t   Asc;                  /**< Additional sense code.                 */
  uint8_t   Ascq;                 /**< Additional sense code qualifier.       */
  uint8_t   Fruc;                 /**< Field replacable unit code.            */
  struct
  {
    uint8_t SenseKeySpecific1 : 7;  /**< Sense key specific field.            */
    uint8_t Sksv              : 1;  /**< Sense key specific field qualifier.  */
  };
  uint8_t SenseKeySpecific2;        /**< Sense key specific field.            */
  uint8_t SenseKeySpecific3;        /**< Sense key specific field.            */
} __attribute__ ((packed)) MSDSCSI_RequestSenseData_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Read Capacity Command Descriptor Block (CDB) typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  uint8_t   OpCode;               /**< Command opcode.                  */
  struct
  {
    uint8_t Obsolete  : 1;        /**< Obsolete, expect any value.      */
    uint8_t Reserved1 : 7;        /**< Reserved, expect 0.              */
  };
  uint32_t  Lba;                  /**< Logical Block (sector) Address.  */
  uint16_t  Reserved2;            /**< Reserved, expect 0.              */
  struct
  {
    uint8_t Pmi       : 1;        /**< Partial medium indicator.        */
    uint8_t Reserved3 : 7;        /**< Reserved, expect 0.              */
  };
  uint8_t   Control;              /**< Control byte.                    */
} __attribute__ ((packed)) MSDSCSI_ReadCapacity_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Read Capacity response data typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  uint32_t LogicalBlockAddress; /**< Last Logical Block (sector) Address on media. */
  uint32_t LogicalBlockLength;  /**< Logical Block (sector) length in bytes.       */
} __attribute__ ((packed)) MSDSCSI_ReadCapacityData_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Read 10 Command Descriptor Block (CDB) typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  uint8_t   OpCode;               /**< Command opcode.                        */
  struct
  {
    uint8_t Obsolete  : 1;        /**< Obsolete, expect any value.            */
    uint8_t FuaNv     : 1;        /**< Force Unit Access non-volatile cache.  */
    uint8_t Reserved1 : 1;        /**< Reserved, expect 0.                    */
    uint8_t Fua       : 1;        /**< Force Unit Access.                     */
    uint8_t Dpo       : 1;        /**< Disable Page Out.                      */
    uint8_t RdProtect : 3;        /**< Read Protect field.                    */
  };
  uint32_t  Lba;                  /**< Logical Block (sector) Address.        */
  struct
  {
    uint8_t GroupNumber : 5;      /**< Group Number field.                    */
    uint8_t Reserved2 : 3;        /**< Reserved, expect 0.                    */
  };
  uint16_t  TransferLength;       /**< Number of blocks (sectors) to transfer.*/
  uint8_t   Control;              /**< Control byte.                          */
} __attribute__ ((packed)) MSDSCSI_Read10_TypeDef;
EFM32_PACK_END()

/**************************************************************************//**
 * @brief SCSI Write 10 Command Descriptor Block (CDB) typedef.
 *****************************************************************************/
EFM32_PACK_START( 1 )
typedef struct
{
  uint8_t   OpCode;               /**< Command opcode.                        */
  struct
  {
    uint8_t Obsolete  : 1;        /**< Obsolete, expect any value.            */
    uint8_t FuaNv     : 1;        /**< Force Unit Access non-volatile cache.  */
    uint8_t Reserved1 : 1;        /**< Reserved, expect 0.                    */
    uint8_t Fua       : 1;        /**< Force Unit Access.                     */
    uint8_t Dpo       : 1;        /**< Disable Page Out.                      */
    uint8_t WrProtect : 3;        /**< Write Protect field.                   */
  };
  uint32_t  Lba;                  /**< Logical Block (sector) Address.        */
  struct
  {
    uint8_t GroupNumber : 5;      /**< Group Number field.                    */
    uint8_t Reserved2 : 3;        /**< Reserved, expect 0.                    */
  };
  uint16_t  TransferLength;       /**< Number of blocks (sectors) to transfer.*/
  uint8_t   Control;              /**< Control byte.                          */
} __attribute__ ((packed)) MSDSCSI_Write10_TypeDef;
EFM32_PACK_END()

/*** MSDSCSI Function prototypes ***/

#if defined( USB_HOST )

bool MSDSCSI_Init(          USBH_Ep_TypeDef *out, USBH_Ep_TypeDef *in );
bool MSDSCSI_Inquiry(       MSDSCSI_InquiryData_TypeDef *data );
bool MSDSCSI_Read10(        uint32_t lba, uint16_t sectors, void *data );
bool MSDSCSI_ReadCapacity(  MSDSCSI_ReadCapacityData_TypeDef *data );
bool MSDSCSI_RequestSense(  MSDSCSI_RequestSenseData_TypeDef *data );
bool MSDSCSI_TestUnitReady( void );
bool MSDSCSI_Write10(       uint32_t lba, uint16_t sectors, const void *data );

#endif

#ifdef __cplusplus
}
#endif

/** @} (end group Drivers) */

#endif /* __MSDSCSI_H */
